package de.jonasblatt.networkapi.rpi.networkapi.rpi.client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.Vector;

import de.jonasblatt.networkapi.rpi.model.ClientModel;
import de.jonasblatt.networkapi.rpi.network.commands.Command;
import de.jonasblatt.networkapi.rpi.network.commands.ServerCommand;
import de.jonasblatt.networkapi.rpi.network.server.ServerImpl;

public class Client implements Runnable {
	private int clientID = 0;
	private ClientModel clientModel;
	private Vector<ClientListener> listeners = new Vector<ClientListener>();
	private int port;
	private ObjectInputStream reader;
	private String serverName;

	private Socket socket;

	private boolean stopFlag;

	private ObjectOutputStream writer;

	public Client(String serverAdress) {
		this.port = ServerImpl.DEFAULTPORT;
		this.serverName = serverAdress;
		this.clientModel = null;
	}

	public Client(String serverAdress, ClientModel clientModel) {
		this.port = ServerImpl.DEFAULTPORT;
		this.serverName = serverAdress;
		this.clientModel = clientModel;
	}

	public Client(String serverAdress, int port) {
		this.port = port;
		this.serverName = serverAdress;
		this.clientModel = null;
	}

	public Client(String serverAdress, int port, ClientModel clientModel) {
		this.port = port;
		this.serverName = serverAdress;
		this.clientModel = clientModel;
	}

	// einen Listener hinzufügen
	public void addListener(ClientListener listener) {
		this.listeners.add(listener);
	}

	private void cl_onClientIdConfirmed() {
		Iterator<ClientListener> iter = this.listeners.iterator();
		while (iter.hasNext()) {
			iter.next().onClientIdConfirmed(this, this.clientID);
		}
	}

	private void cl_onClientNotConnectedToHost() {
		Iterator<ClientListener> iter = this.listeners.iterator();
		while (iter.hasNext()) {
			iter.next().onClientNotConnectedToHost(this);
		}
	}

	private void cl_onServerJoinDenied() {
		Iterator<ClientListener> iter = this.listeners.iterator();
		while (iter.hasNext()) {
			iter.next().onServerJoinDenied(this);
		}
	}

	public void cl_onGpioValue(String gpio, boolean value) {
		Iterator<ClientListener> iter = this.listeners.iterator();
		while (iter.hasNext()) {
			iter.next().onGpioValueArrived(this, gpio, value);
		}
	}

	public void closeConnection() {
		try {
			this.stopFlag = true;
			this.setClientID(-2);
			this.socket.close();
		} catch (IOException e) {
		}
	}

	public void connect() {
		this.createConnection();
	}

	private void createConnection() {
		this.setClientID(0);
		Thread t = new Thread(this);
		this.stopFlag = false;
		t.start();
	}

	public void denyJoinRequest() {
		this.setClientID(-1);
		this.cl_onServerJoinDenied();
		this.closeConnection();
	}

	synchronized public int getClientID() {
		return this.clientID;
	}

	synchronized public ClientModel getClientModel() {
		return this.clientModel;
	}

	synchronized public String getServerName() {
		return this.serverName;
	}

	synchronized public boolean hasId() {
		return (this.getClientID() > 0);
	}

	public boolean isAccepted() {
		return this.hasId();
	}

	public boolean isConnecting() {
		return this.getClientID() == 0;
	}

	// einen Listener entfernen
	public void removeListener(ClientListener listener) {
		this.listeners.remove(listener);
	}

	@Override
	public void run() {
		try {
			this.socket = new Socket(this.serverName, this.port);
			this.writer = new ObjectOutputStream(this.socket.getOutputStream());
			this.writer.flush();
			this.reader = new ObjectInputStream(this.socket.getInputStream());
			this.cl_onClientIdConfirmed();
		} catch (UnknownHostException e1) {
			this.cl_onClientNotConnectedToHost();
			this.stopFlag = true;
			this.clientID = -3;
			return;
		} catch (IOException e1) {
			this.cl_onClientNotConnectedToHost();
			this.clientID = -4;
			this.stopFlag = true;
			return;
		}

		while (!this.stopFlag) {
			if (this.stopFlag) {
				System.out.println("Client stop");
				return;
			}
			ServerCommand cmd;
			try {
				cmd = (ServerCommand) this.reader.readObject();
				cmd.execute(this);
			} catch (ClassNotFoundException e) {
			} catch (IOException e) {
			}
		}
	}

	public boolean sendCommand(Command cmd) {
		if (!this.hasId()) {
			return false;
		}
		try {
			this.writer.writeObject(cmd);
			return true;
		} catch (IOException e) {
			System.out.println("Client sending command failed.");
			this.closeConnection();
			return false;
		}
	}

	synchronized public void setClientID(int id) {
		this.clientID = id;
	}

	synchronized public void setClientModel(ClientModel clientModel) {
		this.clientModel = clientModel;
	}

	synchronized public void setServerName(String serverName) {
		if (!this.isAccepted()) {
			this.serverName = serverName;
		}
	}

}
